package znet

import (
	"fmt"
	"strconv"
	"zinx/utils"
	"zinx/ziface"
)

type MsgHandler struct {
	//存放每个MsgId所对应的处理方法
	Apis map[uint32]ziface.IRouter
	// 业务工作worker池的worker数量
	WorkerPoolSize uint32
	// 负责worker取任务的消息队列
	TaskQueue []chan ziface.IRequest
}

func NewMsgHandler() *MsgHandler {
	return &MsgHandler{
		Apis:           make(map[uint32]ziface.IRouter),
		WorkerPoolSize: utils.GlobalObject.WorkerPoolSize,
		TaskQueue:      make([]chan ziface.IRequest, utils.GlobalObject.WorkerPoolSize),
	}
}

// 调度/执行对应的Router消息处理方法
func (mh *MsgHandler) DoMsgHandler(request ziface.IRequest) {
	// 1. 从Request中找到msgId
	msgId := request.GetMsgId()
	// 根据MsgId ,调度对应的Router业务即可
	router, ok := mh.Apis[msgId]
	if !ok {
		fmt.Println("api msgId = ", msgId, " is not found! Need Register!")
		return
	}

	router.PreHandle(request)
	router.Handle(request)
	router.PostHandle(request)

}

// 为消息添加具体的处理逻辑
func (mh *MsgHandler) AddRouter(msgId uint32, router ziface.IRouter) {
	// 1. 判断当前msg绑定的Api处理方法是否存在
	if _, ok := mh.Apis[msgId]; ok {
		// id 已经注册了
		panic("repeat api, msgID = " + strconv.Itoa(int(msgId)))
	}
	// 2. 添加msg与api的绑定关系
	mh.Apis[msgId] = router
	fmt.Println("Add api MsgID = ", msgId, " success !")
}

// 启动一个worker工作池(开启工作池的动作只能发生一次，一个Zinx框架只能有一个worker工作池)
func (mh *MsgHandler) StartWorkerPool() {
	// 根据WorkerPoolSize分别开启Worker,每个Worker用一个go来承载
	for i := 0; i < int(mh.WorkerPoolSize); i++ {
		// 一个worker被启动
		// 1. 当前的worker对应的chancel消息队列 开辟空间, 第0个worker就用第
		mh.TaskQueue[i] = make(chan ziface.IRequest, utils.GlobalObject.MaxWorkerTaskLen)
		// 2. 启动当前的worker, 阻塞等待消息从chancel传递进来
		go mh.StartOneWorker(i, mh.TaskQueue[i])
	}
}

// 启动一个worker工作流程
func (mh *MsgHandler) StartOneWorker(workerId int, taskQueue chan ziface.IRequest) {
	fmt.Println("current worker id = ", workerId, " is started.....")
	// 不断的阻塞等待对应消息队列的消息
	for {
		select {
		// 如果有消息过来，处理的就是一个客户端的Request, 执行当前的Request所绑定的业务
		case request := <-taskQueue:
			mh.DoMsgHandler(request)
		}
	}
}

// 将消息交给TaskQueue, 由Worker进行处理
func (mh *MsgHandler) SendMsgToTaskQueue(request ziface.IRequest) {
	// 1 将消息平均分配给不同的Worker
	//根据客户端建立的connID 来进行分配
	workderId := request.GetConnection().GetConnID() % mh.WorkerPoolSize
	fmt.Println("Add ConnId = ", request.GetConnection().GetConnID(), " request MsgId=", request.GetMsgId(), " to workder id= ", workderId)

	// 2 将消息发送给对应的Worker的taskqueue即可
	mh.TaskQueue[workderId] <- request
}
